using System;
using System.IO;

namespace SharpTar
{
    public class TarArchive : IDisposable
    {
        private string path = string.Empty;
        private Stream stream = null;

        internal bool anyFileRemoved = false;
        private FilesCollection files = new FilesCollection();
        public FilesCollection Files
        {
            get
            {
                return files;
            }
            set
            {
                files = value;
            }
        }

        public TarArchive()
        {
        }

        public TarArchive(string path)
        {
            if (File.Exists(path))
            {
                parse(File.Open(path, FileMode.Open));
            }

            this.path = path;
        }

        public TarArchive(Stream stream)
        {
            parse(stream);
        }

        public void Save()
        {
            Save(this.path);
        }

        public void Save(string path)
        {
            if (path == string.Empty)
                throw new Exception("Cannot save. No file name specified");

            if (anyFileRemoved || File.Exists(path) == false)
            {
                string orginalPath = path;
                path += ".tmp_tar";

                using (FileStream fs = File.Open(path, FileMode.Create))
                {
                    foreach (TarFile file in files)
                    {
                        byte[] headerBytes = file.Header.GetHeaderValue();
                        fs.Write(headerBytes, 0, headerBytes.Length);
                        long written = file.Read(fs);
                        byte[] align = new byte[512 - written % 512];
                        Array.Clear(align, 0, align.Length);
                        fs.Write(align, 0, align.Length);
                    }

                    byte[] ending = new byte[2 * 512];
                    Array.Clear(ending, 0, ending.Length);
                    fs.Write(ending, 0, ending.Length);
                }

                if(stream != null) this.stream.Dispose();
                if (File.Exists(orginalPath))
                    File.Delete(orginalPath);
                File.Move(path, orginalPath);
            } else
            {
                FileStream fs = stream as FileStream;
                fs.Position = fs.Length - 2 * 512;
                foreach (TarFile file in files)
                {
                    if (file.inArchive)
                        continue;

                    byte[] headerBytes = file.Header.GetHeaderValue();
                    fs.Write(headerBytes, 0, headerBytes.Length);
                    long written = file.Read(fs);
                    byte[] align = new byte[512 - written % 512];
                    Array.Clear(align, 0, align.Length);
                    fs.Write(align, 0, align.Length);
                }

                byte[] ending = new byte[2 * 512];
                Array.Clear(ending, 0, ending.Length);
                fs.Write(ending, 0, ending.Length);

            }
        }

        private void parse(Stream stream)
        {
            this.stream = stream;
            while (stream.Position < stream.Length - 2*512)
            {
                UsTarHeader header = new UsTarHeader();
                byte[] headerBytes = header.GetBytes();
                int read = stream.Read(headerBytes, 0, headerBytes.Length);
                if(read != 512) throw new Exception("Archive is broken");
                header.UpdateHeaderFromBytes();

                TarFile file = new TarFile(stream, stream.Position, true);
                file.Header = header;

                Files.Add(file);

                int extra = file.Header.SizeInBytes % 512 == 0 ? 0 : 1;
                int fileBlocks = (int)(file.Header.SizeInBytes) / 512 + extra;
                stream.Position += fileBlocks * 512;
            }
        }

        public void Close()
        {
            foreach (TarFile file in Files)
            {
                file.Close();
            }

            if (stream != null)
            {
                stream.Dispose();
                stream = null;
            }
        }

        #region IDisposable implementation

        public void Dispose()
        {
            Close();
        }

        #endregion
    }
}

